﻿using Bettzueche.RLGlue.RLTypes;
using Bettzueche.RLGlue.Generics;
using Bettzueche.RLGlue.TaskSpec;

namespace Bettzueche.RLGlue.RLComponent {
    /// <summary>
    /// Die Umwelt im RL-Modell.
    /// <para>
    /// Ein Agent führt auf/in seiner Umwelt ein Aktion a aus. Die Umwelt (bspw. Sensorik)
    /// teilt den Folgezustand s+ und die Belohnung r mit.
    /// </para>
    /// </summary>
    /// <typeparam name="S">The State-Type, implementing IState</typeparam>
    /// <typeparam name="A">The Action-Type, implementing IAction</typeparam>
    /// <typeparam name="R">Any value type for the reward, e.g. int, double</typeparam>
    public interface IEnvironment : AnEnvironment {

        /// <summary>
        /// Initialisierung der Umwelt. Liefert ggf. einen task_spec String.
        /// <para>
        /// Die meisten Welten müssen eine interne Zustandsrepräsentation speichern, weswegen folglich viele</para><para>
        /// Environment-Programme, die Sie schreiben, zu Beginn und am Ende eines Lernexperiments Speicher für Datenstrukturen</para><para>
        /// allokieren und deallokieren müssen.</para><para>
        /// </para><para>
        /// Die Methode <code>Env_init</code> initialisert alle globalen Datenstrukturen und Variablen, die von den Start und Step</para><para>
        /// Funktionen benötigt werden. Zum Beispiel könnte <code>Env_init</code> die Zustandstabelle mit 0 initialisieren und</para><para>
        /// ein numStates X numStates Zustandstransitions Array erzeugen. Optional kann <code>Env_init</code></para><para>
        /// einen <code>task_spec</code>-String definieren. Die Methode <see cref="AnEnvironment.Env_init"/> muss zwar einen</para><para>
        /// String zurückgeben, dieser kann aber leer sein, wenn die task_spec für Ihr Experiment nicht benötigt wird.</para><para>
        /// </para><para>
        /// Der <b>task_spec</b> String enthält eine Versionsnummer, Anzahl aller Zustände, Dimension der Aktionen, Datentypen</para><para>
        /// der Zustände und Aktionen, Wertebereich der Zustände und Aktionen sowie Min- und Max-Wert der Belohnung.</para><para>
        /// </para>
        /// </summary>
        /// <returns>Task_Spec String, ggf. auch leeren String</returns>
        ITaskSpec InitEnvironment();

        /// <summary>
        /// Initialisiert eine neue Episode und liefert den Startzustand.
        /// <para>
        /// Die Methode <code>Env_start</code> ist die erste, die zu Beginn einer Episode aufgerufen wird;</para><para>
        /// <code>Env_start</code> wählt den Initialzustand der Umwelt und liefet die korrespondierende</para><para>
        /// Beobachtung (<see cref="Observation"/>).Beispielweise wählt die folgende Pseudo-Code Implementierung</para><para>
        /// einen zufälligen Zustand für eine Rasterwelt und gibt die Observation zurück:</para><para>
        /// <code>
        /// 1. env_start --> observation
        /// 2.      state = rand()*num_states
        /// 3.      set observation equal to state
        /// 4. return observation
        /// </code>
        /// </para>
        /// </summary>
        /// <returns>Startzustand einer Lern-Episode (bzw. die korrsspondierende Beobachtung)</returns>
        GeneralState StartEnvironment();

        /// <summary>
        /// Führt die angegebene Aktion auf der Umwelt aus und liefert Belohnung, Folgezustand sowie Terminal-Flag.
        /// <para>
        /// Diese Schrittfunktion implementiert den Zustandsübergang und die Belohnungsfunktion. 
        /// In bspw. einer Rasterwelt, ist folgender Pseudocode eine gültige Funktion:
        /// <code>
        /// 1. env_step(action) --> reward, observation, flag 
        /// 2.      newState = updateState(action, state)
        /// 3.      flag = isTerminal(newState)
        /// 3.      reward = calculate reward for newState
        /// 4.      set observation equal to newState
        /// 5.      state = newState
        /// 6. return reward, observation, flag
        ///  </code>
        /// </para>
        /// </summary>
        /// <param name="action">auszuführende Aktion</param>
        /// <param name="outObservation">out Parameter, der den Folgezustand zurückgibt.</param>
        /// <param name="outReward">out Parameter, der den Reward zurückgibt.</param>
        /// <returns>true if reached a terminal state. false otherwise</returns>
        bool StepEnvironment(GeneralAction action, out double outReward, out GeneralState outObservation);

        /// <summary>
        /// Gibt die mit <code>Env_init</code> erzeugten Datenstrukturen wieder frei (dealloc and free).
        /// </summary>
        void CleanupEnvironment();


        /// <summary>
        /// Kommunikation mit der Umwelt, um bspw. Parameter zu setzen.
        /// <para>
        /// Von Zeit zu Zeit werden Sie sich wünschen, benutzerdefinierte Umwelt-Funktionen zum RL-Glue hinzufügen zu können,
        /// die durch das Experiment-Programm aufgerufen werden können.
        /// </para><para>
        /// Die <code>Env_message</code> Methode erlaubt Ihnen prinzipiell Ihre eigene Funktionalität in die
        /// RL-Glue spec. hinzuzufügen. Sie können eine String Nachricht an die Umgebung senden und sie kann mit 
        /// einem String antworten.
        /// </para><para>Zum Beispiel: Sie könnten zufällige Startzustände als Parameter Ihrer Umwelt machen, indem
        /// eine Nachricht nutzen, um diese Eigenschaft umzuschalten:</para>
        /// <code> 
        /// 1. env_message(inMessage) --> outMessage
        /// 2.      if inMessage == "turnOffRandomStarts"  
        /// 3.           randStarts = false
        /// 4.      end 
        /// 5.      if inMessage == "turnOnRandomStarts"  
        /// 6.           randStarts = true
        /// 7.      end 
        /// 8. return ""
        /// </code>
        /// </summary>
        /// <param name="message"></param>
        /// <returns></returns>
        string EnvMessage(string message);
    }
}
